using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Matrix.Configuration.Contracts;
using Matrix.Configuration.DTOs;

namespace Matrix.Configuration.Application.Services
{
    /// <summary>
    /// 配置缓存服务实现
    /// 实现多级缓存：L1本地缓存 + L2分布式缓存
    /// </summary>
    public class ConfigurationCacheService : ApplicationService, IConfigurationCacheService
    {
        private readonly IMemoryCache _localCache;
        private readonly IDistributedCache _distributedCache;
        private readonly ILogger<ConfigurationCacheService> _logger;

        // 缓存键前缀
        private const string CacheKeyPrefix = "Matrix:Configuration:";
        private const string LocalCachePrefix = "Local:";
        private const string DistributedCachePrefix = "Distributed:";

        // 默认过期时间
        private static readonly TimeSpan DefaultExpiration = TimeSpan.FromMinutes(30);
        private static readonly TimeSpan LocalCacheExpiration = TimeSpan.FromMinutes(5);

        public ConfigurationCacheService(
            IMemoryCache localCache,
            IDistributedCache distributedCache,
            ILogger<ConfigurationCacheService> logger)
        {
            _localCache = localCache;
            _distributedCache = distributedCache;
            _logger = logger;
        }

        /// <summary>
        /// 获取缓存项
        /// </summary>
        public async Task<ConfigurationItemDto?> GetAsync(string key, string @namespace = "")
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentException("缓存键不能为空", nameof(key));
            }

            var fullKey = GetFullCacheKey(key, @namespace);
            var localKey = GetLocalCacheKey(fullKey);

            // 1. 尝试从L1本地缓存获取
            if (_localCache.TryGetValue(localKey, out ConfigurationItemDto? localValue))
            {
                _logger.LogDebug("L1缓存命中: {CacheKey}", fullKey);
                return localValue;
            }

            // 2. 尝试从L2分布式缓存获取
            var distributedValue = await _distributedCache.GetAsync(fullKey);
            if (distributedValue != null)
            {
                try
                {
                    var deserializedValue = JsonSerializer.Deserialize<ConfigurationItemDto>(distributedValue);
                    if (deserializedValue != null)
                    {
                        // 回填L1本地缓存
                        _localCache.Set(localKey, deserializedValue, LocalCacheExpiration);
                        _logger.LogDebug("L2缓存命中，已回填L1缓存: {CacheKey}", fullKey);
                        return deserializedValue;
                    }
                }
                catch (JsonException ex)
                {
                    _logger.LogWarning(ex, "反序列化缓存项失败: {CacheKey}", fullKey);
                }
            }

            _logger.LogDebug("缓存未命中: {CacheKey}", fullKey);
            return null;
        }

        /// <summary>
        /// 设置缓存项
        /// </summary>
        public async Task SetAsync(string key, ConfigurationItemDto value, string @namespace = "", TimeSpan? expiration = null)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentException("缓存键不能为空", nameof(key));
            }

            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var fullKey = GetFullCacheKey(key, @namespace);
            var localKey = GetLocalCacheKey(fullKey);
            var expirationTime = expiration ?? DefaultExpiration;

            // 1. 设置L1本地缓存
            _localCache.Set(localKey, value, LocalCacheExpiration);

            // 2. 设置L2分布式缓存
            var serializedValue = JsonSerializer.SerializeToUtf8Bytes(value);
            var cacheOptions = new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = expirationTime
            };

            await _distributedCache.SetAsync(fullKey, serializedValue, cacheOptions);

            _logger.LogDebug("缓存设置成功: {CacheKey}, 过期时间: {Expiration}", fullKey, expirationTime);
        }

        /// <summary>
        /// 移除缓存项
        /// </summary>
        public async Task RemoveAsync(string key, string @namespace = "")
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentException("缓存键不能为空", nameof(key));
            }

            var fullKey = GetFullCacheKey(key, @namespace);
            var localKey = GetLocalCacheKey(fullKey);

            // 1. 移除L1本地缓存
            _localCache.Remove(localKey);

            // 2. 移除L2分布式缓存
            await _distributedCache.RemoveAsync(fullKey);

            _logger.LogDebug("缓存移除成功: {CacheKey}", fullKey);
        }

        /// <summary>
        /// 清除指定命名空间的所有缓存
        /// </summary>
        public async Task ClearNamespaceAsync(string @namespace)
        {
            if (string.IsNullOrWhiteSpace(@namespace))
            {
                throw new ArgumentException("命名空间不能为空", nameof(@namespace));
            }

            var namespacePrefix = $"{CacheKeyPrefix}{@namespace}:";

            // 注意：IMemoryCache和IDistributedCache都不支持按前缀删除
            // 这里我们只能记录日志，实际应用中可能需要维护一个键的索引
            _logger.LogWarning("清除命名空间缓存功能需要实现键索引机制: {Namespace}", @namespace);

            // 对于演示，我们只能清理分布式缓存中的特定键
            // 在生产环境中，建议使用Redis的SCAN命令或维护键索引
        }

        /// <summary>
        /// 清除所有配置缓存
        /// </summary>
        public async Task ClearAllAsync()
        {
            // 清除本地缓存
            if (_localCache is MemoryCache memoryCache)
            {
                memoryCache.Compact(1.0); // 清除所有缓存项
            }

            // 注意：分布式缓存无法清除所有键，需要实现键索引机制
            _logger.LogWarning("清除所有缓存功能需要实现键索引机制");
        }

        /// <summary>
        /// 批量获取缓存项
        /// </summary>
        public async Task<Dictionary<string, ConfigurationItemDto>> GetBatchAsync(List<string> keys, string @namespace = "")
        {
            if (keys == null || keys.Count == 0)
            {
                return new Dictionary<string, ConfigurationItemDto>();
            }

            var result = new Dictionary<string, ConfigurationItemDto>();
            var tasks = new List<Task>();

            foreach (var key in keys)
            {
                tasks.Add(Task.Run(async () =>
                {
                    try
                    {
                        var value = await GetAsync(key, @namespace);
                        if (value != null)
                        {
                            lock (result)
                            {
                                result[key] = value;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "批量获取缓存项失败: {Key}", key);
                    }
                }));
            }

            await Task.WhenAll(tasks);

            _logger.LogDebug("批量获取缓存完成，请求数量: {RequestedCount}, 返回数量: {ResultCount}",
                keys.Count, result.Count);

            return result;
        }

        /// <summary>
        /// 批量设置缓存项
        /// </summary>
        public async Task SetBatchAsync(Dictionary<string, ConfigurationItemDto> items, string @namespace = "", TimeSpan? expiration = null)
        {
            if (items == null || items.Count == 0)
            {
                return;
            }

            var tasks = new List<Task>();

            foreach (var kvp in items)
            {
                tasks.Add(SetAsync(kvp.Key, kvp.Value, @namespace, expiration));
            }

            await Task.WhenAll(tasks);

            _logger.LogDebug("批量设置缓存完成，设置数量: {Count}", items.Count);
        }

        /// <summary>
        /// 检查缓存项是否存在
        /// </summary>
        public async Task<bool> ExistsAsync(string key, string @namespace = "")
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                return false;
            }

            var value = await GetAsync(key, @namespace);
            return value != null;
        }

        /// <summary>
        /// 刷新缓存项（延长过期时间）
        /// </summary>
        public async Task RefreshAsync(string key, string @namespace = "", TimeSpan? expiration = null)
        {
            var value = await GetAsync(key, @namespace);
            if (value != null)
            {
                await SetAsync(key, value, @namespace, expiration);
                _logger.LogDebug("缓存刷新成功: {CacheKey}", GetFullCacheKey(key, @namespace));
            }
        }

        /// <summary>
        /// 获取缓存统计信息
        /// </summary>
        public async Task<CacheStatisticsDto> GetStatisticsAsync()
        {
            var statistics = new CacheStatisticsDto
            {
                LastUpdateTime = DateTime.UtcNow,
                TotalItems = 0, // 默认值，实际应用中应该维护计数器
                HitCount = 0,
                MissCount = 0,
                SetCount = 0,
                RemoveCount = 0,
                ClearCount = 0,
                ExpiredCount = 0,
                AverageLifetimeSeconds = 0,
                MemoryUsageBytes = 0
            };

            // 如果可能，获取本地缓存的统计信息
            if (_localCache is MemoryCache memoryCache)
            {
                try
                {
                    var cacheStats = typeof(MemoryCache)
                        .GetProperty("EntriesCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                        ?.GetValue(memoryCache);

                    var count = cacheStats?.GetType()
                        .GetProperty("Count")?.GetValue(cacheStats);

                    if (count is int itemCount)
                    {
                        statistics.TotalItems = itemCount;
                    }
                }
                catch
                {
                    // 无法获取统计信息时保持默认值
                }
            }

            await Task.CompletedTask; // 异步签名兼容
            return statistics;
        }

        #region 私有方法

        private static string GetFullCacheKey(string key, string @namespace)
        {
            return $"{CacheKeyPrefix}{@namespace}:{key}";
        }

        private static string GetLocalCacheKey(string fullKey)
        {
            return $"{LocalCachePrefix}{fullKey}";
        }

        #endregion
    }
}